﻿using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;

public static class FixedFloatMath
{
	/// Newton 法 1
	public static ulong Sqrt (ulong x)
	{
		if (x == 0) return 0;

		ulong org = x;
		ulong x1 = x - 1;
		int s = 1;
		ulong g0,g1;

		// 初值设定  通常将初始值设为1，但是只有1的开方才会是1，通过预处理找到更精确地初始值a[n]
		// 此处理能大幅度减少迭代次数 最多迭代4次即可

		if (x1 > 2147483646) 	{ s += 16; x1 >>= 32;}
		if (x1 > 65535) 		{ s += 8; x1 >>= 16;}
		if (x1 > 255)   		{ s += 4; x1 >>= 8;}
		if (x1 > 15)    		{ s += 2; x1 >>= 4;}
		if (x1 > 3)    			{ s += 1; x1 >>= 2;}

		//迭代
		g0 = (ulong)1 << s;
		g1 = (g0 + (x >> s)) >> 1;
		while(g1 < g0)
		{
			g0 = g1;
			//UnityEngine.Debug.Log("g0: " + g0);
			g1 = (g0 + x/g0) >> 1;
		}
		return g0;
	}

	/// 小数位被截断，不会四舍五入
	public static FixedFloat Sqrt (FixedFloat v)
	{
		return FixedFloat.FixedFloatFromRawValue((long) (FixedFloatMath.Sqrt((ulong)v.rawValue) * FixedFloat.denominator_sqrt) );
	}

	// 定点数 自然对数
	public static readonly FixedFloat E = new FixedFloat(2.7183);
	// 定点数 圆周率
	public static readonly FixedFloat PI = new FixedFloat(3.1416);
	// 定点数 圆周率
	public static readonly FixedFloat PI_Two = new FixedFloat(6.2832);
	// 定点数 半圆周率
	public static readonly FixedFloat PI_Half = new FixedFloat(1.5708);

	public static long Abs (long value)
	{
		if (value == -9223372036854775808) {
			throw new OverflowException ("Value is too small.");
		}
		return (value >= 0) ? value : (-value);
	}

	public static FixedFloat Abs (FixedFloat value)
	{
		return value.Abs();
	}

	public static int Abs (int value)
	{
		if (value == -2147483648) {
			throw new OverflowException ("Value is too small.");
		}
		return (value >= 0) ? value : (-value);
	}

    public static FixedFloat Max(FixedFloat val1, FixedFloat val2)
    {
        return (val1 <= val2) ? val2 : val1;
    }
    
    public static FixedFloat Min(FixedFloat val1, FixedFloat val2)
    {
        return (val1 >= val2) ? val2 : val1;
    }

    public static int Clamp (int value, int min, int max)
	{
		if (value < min) value = min;
		else if (value > max) value = max;

		return value;
	}
    public static FixedFloat Clamp(FixedFloat value, FixedFloat min, FixedFloat max)
    {
        if (value < min) value = min;
        else if (value > max) value = max;

        return value;
    }

    public static FixedFloat Deg2Rad = 0.0174532924F;// new FixedFloat(1745);
    public static FixedFloat Rad2Deg = 57.29578F; //new FixedFloat(572950);
    /// 角度转弧度
    public static FixedFloat Angle2Radian (FixedFloat angle)
	{
        return angle * Deg2Rad;// PI / 180;
	}
	/// 弧度转角度
	public static FixedFloat Radian2Angle (FixedFloat radian)
	{
        return radian * Rad2Deg;// 180 / PI;
	}

	// sin
	const  int SIN_RADIAN_DELTA = 7;
	const  int SIN_ARRAY_LEN = 62832 / SIN_RADIAN_DELTA;// 2PI / delta
	const  int SIN_ARRAY_INDEX_MAX = SIN_ARRAY_LEN - 1;

	// cos
	const  int COS_RADIAN_DELTA = 7;
	const  int COS_ARRAY_LEN = 62832 / SIN_RADIAN_DELTA;// 2PI / delta
	const  int COS_ARRAY_INDEX_MAX = COS_ARRAY_LEN - 1;

	// tan 同时用于计算atan
	const  int TAN_RADIAN_DELTA = 2;
	const  int TAN_ARRAY_LEN = 15708 / TAN_RADIAN_DELTA;// 0.5PI / delta
	const  int TAN_ARRAY_INDEX_MAX = TAN_ARRAY_LEN - 1;

	// asin
	const  int ASIN_VALUE_DELTA = 10;
	const  int ASIN_ARRAY_INDEX_MAX = (int)(20000 / ASIN_VALUE_DELTA); // 2 / delta

	// acos
	const  int ACOS_VALUE_DELTA = 10;
	const  int ACOS_ARRAY_INDEX_MAX = (int)(20000 / ACOS_VALUE_DELTA); // 2 / delta

	public static FixedFloat Sin (FixedFloat radian)
	{
		// 弧度重复范围 sin (0 ~ 2PI)
		radian = radian.Repeat(PI_Two);
		// 取索引
		int index = radian.IndexOfInterval_Factor(SIN_RADIAN_DELTA);
		// 索引限定
		index = Clamp(index, 0, SIN_ARRAY_INDEX_MAX);
		// 读表取值
		return new FixedFloat(FixedFloatTable._sinArray[index]);
	}

	public static FixedFloat Cos (FixedFloat radian)
	{
		// 弧度重复范围 cos (0 ~ 2PI)
		radian = radian.Repeat(PI_Two);
		// 取索引
		int index = radian.IndexOfInterval_Factor(COS_RADIAN_DELTA);
		// 索引限定
		index = Clamp(index, 0, COS_ARRAY_INDEX_MAX);
		// 读表取值
		return new FixedFloat(FixedFloatTable._cosArray[index]);
	}

	public static FixedFloat Tan (FixedFloat radian)
	{
		// 弧度重复范围 tan (-0.5PI ~ 0.5PI)
		radian = radian.Repeat(-PI_Half, PI);

		// 负值确定
		bool neg = false;
		if (radian < 0)
		{
			neg = true;
			radian = -radian;
		}

		// 取索引
		int index = radian.IndexOfInterval_Factor(TAN_RADIAN_DELTA);
		// 索引限定
		index = Clamp(index, 0, TAN_ARRAY_INDEX_MAX);

		// 读表取值
		long value = FixedFloatTable._tanArray[index];

		// 负值确认
		if (neg)
		{
			value = -value;
		}

		// 返回数据
		return new FixedFloat(value);
	}

	public static FixedFloat Asin (FixedFloat value)
	{
		// 范围校验 sin value -1 ~ +1
		if (value < FixedFloat.NegativeOne || value > FixedFloat.One)
		{
			throw new System.Exception("asin值越界: " + value);
		}
		// 取索引
		int index = value.IndexOfInterval_Factor(-10000, ASIN_VALUE_DELTA);
		// 索引限定
		index = Clamp(index, 0, ASIN_ARRAY_INDEX_MAX);
		// 读表取值
		return new FixedFloat(FixedFloatTable._asinArray[index]);
	}

	public static FixedFloat Acos (FixedFloat value)
	{
		// 范围校验 sin value -1 ~ +1
		if (value < FixedFloat.NegativeOne || value > FixedFloat.One)
		{
			throw new System.Exception("acos值越界: " + value);
		}
		// 取索引
		int index = value.IndexOfInterval_Factor(-10000, ACOS_VALUE_DELTA);
		// 索引限定
		index = Clamp(index, 0, ACOS_ARRAY_INDEX_MAX);
		// 读表取值
		return new FixedFloat(FixedFloatTable._acosArray[index]);
	}

	public static FixedFloat Atan (FixedFloat value)
	{
		bool nav = false;// 负
		if (value < 0)
		{
			nav = true;
			value = -value;
		}

		int index = Find(value.rawValue, FixedFloatTable._tanArray, 0, FixedFloatTable._tanArray.Length);

		int factor = TAN_RADIAN_DELTA * index;
		if (nav)
		{
			factor = -factor;
		}
		FixedFloat radian = new FixedFloat(factor);

		return radian;
	}

    public static FixedFloat Atan2(FixedFloat y, FixedFloat x)
    {
        if (y == 0 && x == 0)
        {
            return 0;
        }
        else if(y > 0 && x == 0)
        {
            return PI / 2;
        }
        else if (y < 0 && x == 0)
        {
            return -1 * PI / 2;
        }

        FixedFloat sum = Atan(y / x);

        if (x > 0)
        {
            return sum;
        }
        else if (y >= 0 && x < 0)
        {
            return sum + PI;
        }
        else if (y < 0 && x < 0)
        {
            return sum - PI;
        }
        else
        {
            return 0;
        }
    }
    
    // 快速查找 区间值
    private static int Find (long value, long[] array, int index, int count)
    {
	    if (count == 1)
	    {
		    return index;
	    }
	    else if (count == 2)
	    {
		    if (value < (array[index] + array[index + 1]) / 2)// 中间值比较
		    {
			    return index;
		    }
		    else
		    {
			    return index + 1;
		    }
	    }

	    int middleRgt = index + count / 2;// 中间索引 左边
	    int middleLft = middleRgt - 1;// 中间索引 右边


	    if (value == array[middleLft])
	    {
		    return middleLft;
	    }
	    else if (value == array[middleRgt])
	    {
		    return middleRgt;
	    }

	    if (value < (array[middleLft] + array[middleRgt]) / 2)
	    {
		    return Find(value, array, index, middleRgt - index);
	    }
	    else
	    {
		    return Find(value, array, middleRgt, count - (middleRgt - index));
	    }
    }
    public static FixedFloat Pow(FixedFloat x,int n)
    {
        if (n == 0)
            return 1;

        FixedFloat res = x;
        for (int i = 0; i < n-1; i++)
        {
            x *= x;
        }
        return x;
    }

    public static FixedFloat Rough(FixedFloat num, int precision)
    {
	    FixedFloat numpower = num * precision ;

        int resInt = numpower.roundToInt;

        FixedFloat ret = (FixedFloat)resInt / precision;
        
//        GameDebug.Log($" res:{resInt}  num:{num}  ret:{ret}");
        return ret;
    }
}
